package logic;

import java.io.Serializable;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Set;

public class Country implements Serializable {
	
	private static final long serialVersionUID = 1L;
	private String name;
	private Player owner;
	private HashMap<String, Country> neighbors;
	private int soldiers;
	
	/**
	 * deep cloning this instance: all referenced types are cloned
	 */
	public Country deepClone(){
		Country country = new Country(this.name);
		country.setOwner(owner);
		for(Country neighbor: this.neighbors.values()){
			country.addNeighbor(neighbor);
		}
		country.soldiers = this.soldiers;
		return country;
	}
	
	/**
	 * @return the soldiers
	 */
	public int getSoldiers() {
		return soldiers;
	}

	public Country(String name) {
		this.name=name;
		owner = null;
		neighbors = new HashMap<String, Country>();
	}
	
	public void addNeighbor(Country country){
		if (!neighbors.containsKey(country.toString()))
			neighbors.put(country.toString(), country);
	}
	
	/**
	 * @return Name of country
	 */
	public String toString(){
		return name;
	}
	
	/**
	 * @return true iff this country is equal the argument country
	 */
	public boolean equals(Object country){
		if(country == null)
			return false;
		if(!(country instanceof Country))
			return false;
		
		return this.toString().equals(((Country)country).toString());
	}
	
	/** 
	 * @return true if country is owned by a player
	 */
	public boolean hasOwner(){
		return owner != null && owner.getPlayerName().length() != 0;
	}

	/**
	 * @return owner
	 */
	public Player getOwner() {
		return owner;
	}

	/**
	 * DO NOT USE THIS FUNCTION.
	 * (and do not delete it)
	 * sets the country to be owned by the player
	 * Be careful, this doesn't update the country in reference in the player
	 */
	public void setOwner(Player owner) {
		this.owner = owner;
	}
	
	/**
	 * DO NOT USE THIS FUNCTION.
	 * (and do not delete it)
	 * add amount soldiers into this country WITHOUT updating the player
	 */
	public void addSoldiers(int amount){
		this.soldiers += amount;
	}
	
	/**
	 * DO NOT USE THIS FUNCTION.
	 * (and do not delete it)
	 * sets the amount soldiers into this country WITHOUT updating the player
	 */
	public void setSoldiersAmount(int amount){
		this.soldiers = amount;
	}

	/**
	 * @return an hash map of this country's neighbors
	 */
	public HashMap<String, Country> getNeighbors() {
		return neighbors;
	}

	/**
	 * @return true iff this country is owned by a player
	 */
	public boolean isOccupied(){
		return (this.owner != null);
	}

	/**
	 * @return this country's name
	 */
	public String getName() {
		return name;
	}
	
	/**
	 * deduct a soldier from this country
	 */
	public void killSoldier(){
		this.soldiers--;
	}
	
	public void reduceSoldiers(int amount) {
		this.soldiers -= amount;
	}
	
	/**
	 * 
	 * @param otherCountry
	 * @param amount
	 * moves soldiers from country to other country
	 */
	public void moveSoldiers(Country otherCountry, int amount){
		this.soldiers -= amount;
		otherCountry.setSoldiersAmount(otherCountry.getSoldiers() + amount);
	}

	/**
	 * returns true iff country1 and country2 are neighbors
	 * @param country1
	 * @param country2
	 */
	public static boolean isNeighbors(Country country1,
			Country country2) {
		return (country1.getNeighbors().get(country2.getName()) != null);
	}
	
	/**
	 * 
	 * @return a Collection of Country which are 
	 * possible attack targets for the owner of the
	 * current country. 
	 * @MAY contain duplicates
	 * @USE with canAttack()
	 */
	public ArrayList<Country> getAttackTargets()
	{
		ArrayList<Country> rs = new ArrayList<Country>();
		for(Country neighbor : getNeighbors().values())
		{
			if(neighbor.hasOwner() && neighbor.getOwner().getPlayerName().compareTo( owner.getPlayerName()) != 0)
				rs.add(neighbor);
		}
		return rs;
	}
	/**
	 * 
	 * @return true if this country can initiate an attack
	 * @USE with hasAttackTargets()
	 */
	public boolean canAttack()
	{
		return hasOwner() && getSoldiers() > 1;
	}

	public boolean hasNeighbor(Country other) {
		return neighbors.containsKey(other.getName());
	}
	
	/**
	 * Recursive procedure that receive a set and fill it with the connected countries
	 * (connected countries - countries you can move soldiers to)
	 * @param countries - the set of the connected countries
	 * @param owner - the owner of the country we are checking right now.
	 */
	public void getConnectedCountries(Set<Country> countries, Player owner) {
		for (Country country : this.getNeighbors().values()) {
			if (!country.hasOwner()) 
				continue;
			if (country.getOwner().equals(owner)) {
				if (countries.add(country)) {
					country.getConnectedCountries(countries, owner);
				}
			}
		}
	}
	
	/**
	 * @return true if this country can move soldiers to another one.
	 */
	public boolean canMove(Country destCountry) {
		if (this.getSoldiers() < 2) return false;
		Set<Country> countries = new HashSet<Country>();
		Player owner = this.getOwner();
		getConnectedCountries(countries, owner);
		countries.remove(this);
		return countries.contains(destCountry);
		
	}
	
}
